package org.midy.base;

import org.midy.infrastructure.tools.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ServiceMain {
    public static void main(String[] args) {
        int port = 5058;
        int preLength = 8;
        Charset charset = Charset.forName("UTF-8");
        ServerSocket  serverSocketListen = initServerListen(port);
        ThreadPoolExecutor executor = initProcessPools();
        try {
            do {
                Logger.info(" waiting to accept ...");
                try {
                    Socket accept = serverSocketListen.accept();
                    accept.setKeepAlive(true);
                    String address = String.format("%s:%d", accept.getInetAddress().getHostAddress(), accept.getPort());
                    recordLog(accept);
                    executor.submit( () -> {
                        try (
                            InputStream inputStream = accept.getInputStream();
                            OutputStream outputStream = accept.getOutputStream()
                        ){
                            while (true) {
                                byte[] read = read(inputStream, preLength);
                                String request = new String(read, charset);
                                String response = write(outputStream, preLength, charset);
//                                Logger.info("remote:[%s], request:[%s], response:[%s]", address, request, response);
                            }
                        } catch (Throwable t) {
                            Logger.error("remote:["+address+"] is error end", t);
                        } finally {
                            if(accept != null) {
                                try {
                                    accept.close();
                                } catch (Exception t) {
                                    Logger.error("close is exception:",  t);
                                }
                            }
                            Logger.info("remote:[%s] is end", address);
                        }
                    });
                } catch ( Throwable t) {
                    Logger.warn(" submit Task is fail", t);
                }
            } while (true);
        } finally {
            executor.shutdown();
        }
    }

    private static void recordLog(Socket accept) {
        if(accept == null) {
            return;
        }
        int port = accept.getPort();
        InetAddress inetAddress = accept.getInetAddress();
        String ip = inetAddress == null? "": inetAddress.getHostAddress();
        Logger.info("new Connection, remote_ip: %s, remote_port: %d", ip, port);
    }

    private static ThreadPoolExecutor initProcessPools() {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10,  100, TimeUnit.HOURS, new LinkedBlockingQueue<>());
        threadPoolExecutor.prestartAllCoreThreads();
        return threadPoolExecutor;
    }

    private static ServerSocket initServerListen(int port) {
        try {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(port));
            return serverSocket;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static byte[] read(InputStream inputStream, int preLength) throws IOException {
        byte[] header = new byte[preLength];
        int off = 0;
        while ((off = (inputStream.read(header, off, preLength - off) + off)) < preLength) {
            Logger.info(" read head size: " + off);
        }

        Integer msgSize = new Integer(new String(header, Charset.forName("UTF-8")));
        if (msgSize == 0) {
            return null;
        }

        byte[] body = new byte[msgSize];
        off = 0;
        while ((off = (inputStream.read(body, off, msgSize - off) + off)) < msgSize) {
            Logger.info(" read body size: " + off);
        }
        return body;
    }

    private static String write(OutputStream outputStreamm, int preLength, Charset charset) throws Exception{
        String name = Thread.currentThread().getName();
        String response = String.format(" type:response, name: %s, 我是响应信息哟", name);
        byte[] responseBytes = response.getBytes(charset);
        byte[] headerBytes = ConnectClient.generateHeader(preLength, responseBytes.length);
        outputStreamm.write(headerBytes);
        outputStreamm.write(responseBytes);
        return response;
    }

}
